﻿using InTheHand.Net;
using Microsoft.Extensions.Logging;
using MyTRCP.Common.Models;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using System.Net;
using System.Net.Sockets;

namespace MyTRCP.Common.MyProtocol
{
    public class BridgeSide : IDisposable
    {
        private readonly int _port;
        private volatile Socket _bridgeSock4;
        private volatile Socket _bridgeSock6;
        private readonly byte[] _buf4;
        private readonly byte[] _buf6;
        private readonly Dictionary<Guid, SessionInfo> _validSessions;
        private readonly Dictionary<string, AuthStageInfo> _authStages;
        private readonly BlockingQueue<byte[]> _devCtrlRxQueue;
        private readonly BlockingQueue<byte[]> _devAudioRxQueue;
        private readonly CancellationTokenSource _cts;
        private readonly AsymmetricCipherKeyPair _ecBridgeIdAuthKeyPair;
        private readonly BlockingQueue<byte[]> _devCtrlTxQueue;
        private readonly BlockingQueue<byte[]> _devAudioTxQueue;
        private int _eachPayloadRepeatTimes = 3;
        private readonly List<ECPublicKeyParameters> _trustedClientPublicKeys;
        private bool _recv4Running;
        private bool _recv6Running;
        private bool _sendCtrlRunning;
        private bool _sendAudioRunning;
        private bool _takeQueueToSendRunning;
        private readonly ILogger<BridgeSide> _logger;
        private readonly BlockingQueue<MyWaitToSendDataInfo> _waitToSendQueue;
        private readonly Func<DevLinkOperationTypeEnum, BluetoothAddress?, Task<int>> _remoteDevLinkOp;
        private readonly Func<Task<RemoteDevLinkStatusArgs>> _getRemoteDevLinkStatus;
        private readonly Func<int, Task<IReadOnlyCollection<BtDevInfo>?>> _discoverBtDevs;
        private readonly Dictionary<LinkControlCmdEnum, DevLinkOperationTypeEnum> _lccDlotDic;
        public bool RunningStatus
        {
            get
            {
                return _recv4Running || _recv6Running || _sendAudioRunning || _sendCtrlRunning || _takeQueueToSendRunning;
            }
        }

        public int EachPayloadRepeatTimes { get => _eachPayloadRepeatTimes; set => _eachPayloadRepeatTimes = value; }

        ~BridgeSide()
        {
            Dispose();
        }
        public BridgeSide(ILogger<BridgeSide> logger, int port, AsymmetricCipherKeyPair ecBridgeIdAuthkeyPair, List<ECPublicKeyParameters> trustedClientPublicKeys, BlockingQueue<byte[]> devCtrlRxQ, BlockingQueue<byte[]> devAudioRxQ, BlockingQueue<byte[]> devCtrlTxQ, BlockingQueue<byte[]> devAudioTxQ, Func<DevLinkOperationTypeEnum, BluetoothAddress?, Task<int>> linkOp, Func<Task<RemoteDevLinkStatusArgs>> getLinkStatus, Func<int, Task<IReadOnlyCollection<BtDevInfo>?>> discoverBtDevs, int repeatTimes = 3)
        {
            _eachPayloadRepeatTimes = repeatTimes;
            _logger = logger;
            _port = port;
            _buf4 = new byte[2048];
            _buf6 = new byte[2048];
            _bridgeSock4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _bridgeSock4.Bind(new IPEndPoint(IPAddress.Any, _port));
            _bridgeSock6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
            _bridgeSock6.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
            _cts = new CancellationTokenSource();
            _cts.Token.ThrowIfCancellationRequested();
            _devCtrlRxQueue = devCtrlRxQ;
            _devAudioRxQueue = devAudioRxQ;
            _devCtrlTxQueue = devCtrlTxQ;
            _devAudioTxQueue = devAudioTxQ;
            _validSessions = new Dictionary<Guid, SessionInfo>();
            _authStages = new Dictionary<string, AuthStageInfo>();
            _ecBridgeIdAuthKeyPair = ecBridgeIdAuthkeyPair;
            _trustedClientPublicKeys = trustedClientPublicKeys;
            _waitToSendQueue = new BlockingQueue<MyWaitToSendDataInfo>();
            _remoteDevLinkOp = linkOp;
            _getRemoteDevLinkStatus = getLinkStatus;
            _discoverBtDevs = discoverBtDevs;
            _lccDlotDic = new Dictionary<LinkControlCmdEnum, DevLinkOperationTypeEnum>
            {
                { LinkControlCmdEnum.CONNECT_CONTROL_CHANNEL, DevLinkOperationTypeEnum.ConnectControlLink },
                { LinkControlCmdEnum.DISCONNECT_CONTROL_CHANNEL, DevLinkOperationTypeEnum.DisconnectControlLink },
                { LinkControlCmdEnum.CONNECT_AUDIO_CHANNEL, DevLinkOperationTypeEnum.ConnectAudioLink },
                { LinkControlCmdEnum.DISCONNECT_AUDIO_CHANNEL, DevLinkOperationTypeEnum.DisconnectAudioLink }
            };
        }
        public void Start()
        {
            if (_cts.TryReset())
                _logger.LogDebug("cts resetted.");
            Task.Run(ReceiveDataViaIPv4Async);
            Task.Run(ReceiveDataViaIPv6Async);
            Task.Run(SendDevCtrlDataAsync);
            Task.Run(SendDevAudioDataAsync);
            Task.Run(TakeQueueToSendAsync);
        }
        public void Stop()
        {
            _devCtrlRxQueue.CancelTake();
            _devAudioRxQueue.CancelTake();
            _waitToSendQueue.CancelTake();
            _cts.Cancel();
        }
        private async void ReceiveDataViaIPv4Async()
        {
            _recv4Running = true;
            EndPoint ep = new IPEndPoint(IPAddress.Any, 0);
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        var result = await _bridgeSock4.ReceiveFromAsync(_buf4, ep, _cts.Token);
                        ProcessOnReceived(_buf4, result.ReceivedBytes, (IPEndPoint)result.RemoteEndPoint);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpBridgeSide ReceiveDataViaIPv4Async in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpBridgeSide ReceiveDataViaIPv4Async in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpBridgeSide ReceiveDataViaIPv4Async Excpetion:{ex.Message}");
            }
            _recv4Running = false;
        }
        private async void ReceiveDataViaIPv6Async()
        {
            _recv6Running = true;
            EndPoint ep = new IPEndPoint(IPAddress.IPv6Any, 0);
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        var result = await _bridgeSock6.ReceiveFromAsync(_buf6, ep, _cts.Token);
                        ProcessOnReceived(_buf6, result.ReceivedBytes, (IPEndPoint)result.RemoteEndPoint);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpBridgeSide ReceiveDataViaIPv6Async in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpBridgeSide ReceiveDataViaIPv6Async in-loop Excpetion:{ex.Message}.ep={ep}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpBridgeSide ReceiveDataViaIPv6Async Excpetion:{ex.Message}");
            }
            _recv6Running = false;
        }
        private void EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum code, byte[] data, LinkControlCmdEnum cmd, Guid sessionId, IPEndPoint remoteEP, ParametersWithIV? cryptoKey = null, bool needRemoveSession = false)
        {
            LinkControlResponsePayload pcrp = new LinkControlResponsePayload(code, data);
            LinkControlPayload lcp_r = new LinkControlPayload(true, cmd, pcrp.WriteToBytes());
            byte[] lcpBytes = lcp_r.WriteToBytes();
            if (cryptoKey != null)
            {
                lcpBytes = MyProtocolCryptoHelper.SM4Encrypt(lcpBytes, cryptoKey);
            }
            _waitToSendQueue.Add(new MyWaitToSendDataInfo(sessionId, false, true, cryptoKey != null, false, PayloadTypeEnum.LINK_CONTROL, lcpBytes, remoteEP, needRemoveSession));
        }
        private async void ProcessOnReceived(byte[] buf, int receivedLen, IPEndPoint ep)
        {
            if (receivedLen == 0)
            {
                _logger.LogDebug($"从{ep}接收到长度为0的数据，忽略");
                return;
            }
            MyProtocolPayload payload = new MyProtocolPayload(buf, 0, receivedLen);
            if (payload.ValidCheck())
            {
                if (payload.IsClientSideSent)
                {
                    if (payload.SessionId == Guid.Empty)//new client
                    {
                        if (payload.DataType == PayloadTypeEnum.LINK_CONTROL)
                        {
                            LinkControlPayload lcp = new LinkControlPayload(payload.Data);
                            string epStr = ep.ToString();
                            _logger.LogDebug($"收到lcp载荷,cmd={lcp.Cmd},对方端点={ep}");
                            if (_authStages.TryGetValue(epStr, out AuthStageInfo? value))
                            {
                                if ((!payload.IsThisDatagramChangeId && payload.DatagramId > value.LastRecvDatagramId) || (payload.IsThisDatagramChangeId && !value.LastRecvDatagramChangeId))
                                {
                                    value.LastRecvDatagramId = payload.DatagramId;
                                    value.LastRecvDatagramChangeId = payload.IsThisDatagramChangeId;
                                    if (value.AuthStage == MyProtocolAuthStageEnum.BRIDGE_SAY_HELLO && lcp.Cmd == LinkControlCmdEnum.CLIENT_AUTH)//服务端auth
                                    {
                                        ClientAuthPayload cap = new ClientAuthPayload(lcp.Data);
                                        value.AuthStage = MyProtocolAuthStageEnum.CLIENT_REQUEST_AUTH;
                                        if (MyProtocolCryptoHelper.CheckChallengeResponseCorrect(cap.ClientIdAuthResponse, value.RemoteIdAuthChallengeRandom) && value.LocalKeyAgreementPrivateKey != null && value.RemoteKeyAgreementPublicKey != null)
                                        {
                                            byte[] bridgeChallengeResp = MyProtocolCryptoHelper.ChallengeResponse(cap.BridgeIdAuthChallenge, _ecBridgeIdAuthKeyPair.Private);
                                            ParametersWithIV sessionKey = MyProtocolCryptoHelper.GenerateSM4KeyFromBytes(MyProtocolCryptoHelper.GetECAgreementResult(value.LocalKeyAgreementPrivateKey, value.RemoteKeyAgreementPublicKey).ToByteArray());
                                            BridgeAuthPayload bap = new BridgeAuthPayload() { BridgeIdAuthResponse = bridgeChallengeResp, BridgeIdAuthResponseLength = (ushort)bridgeChallengeResp.Length };
                                            do
                                            {
                                                MyProtocolCryptoHelper.SecRandom.NextBytes(bap.SessionIdBytes);
                                                if (!_validSessions.ContainsKey(bap.SessionId))
                                                {
                                                    SessionInfo csi = new SessionInfo() { LastCommDateTime = DateTime.Now, LastEP = ep, LastRecvDatagramId = payload.DatagramId, LastRecvDatagramChangeId = payload.IsThisDatagramChangeId, SessionId = bap.SessionId, SessionKey = sessionKey, RepeatTimes = _eachPayloadRepeatTimes };
                                                    _validSessions.Add(bap.SessionId, csi);
                                                    break;
                                                }
                                            }
                                            while (true);
                                            EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, bap.WriteToBytes(), lcp.Cmd, payload.SessionId, ep, null, true);
                                            value.AuthStage = MyProtocolAuthStageEnum.BRIDGE_REPLY_AUTH;
                                            _logger.LogDebug($"bridge auth stage change to {value.AuthStage}");
                                        }
                                        else
                                        {
                                            EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.ACCESS_DENIED, [], lcp.Cmd, payload.SessionId, ep, null, true);
                                            _logger.LogDebug("bridge check challenge failed");
                                        }
                                    }
                                    else
                                    {
                                        EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.ACCESS_DENIED, [], lcp.Cmd, payload.SessionId, ep, null, true);
                                        _logger.LogDebug($"bridge check auth stage incorrect, except bridge_say_hello but {value.AuthStage}");
                                    }
                                }
                            }
                            else
                            {
                                if (lcp.Cmd == LinkControlCmdEnum.HELLO)//服务端hello
                                {
                                    ClientHelloPayload chp = new ClientHelloPayload(lcp.Data);
                                    if (chp.HashAlgorithmType == HashAlgorithmTypeEnum.SM3 && chp.IdAuthCryptoType == IdAuthCryptoTypeEnum.SM2 && chp.KeyAgreementAlgorithmType == KeyAgreementAlgorithmTypeEnum.ECDH)
                                    {
                                        ECPublicKeyParameters remoteIdPublicKey = MyProtocolCryptoHelper.ECPublicKeyFromMyBytes(chp.ClientIdAuthPublicKeyX, chp.ClientIdAuthPublicKeyY);
                                        if (MyProtocolCryptoHelper.CheckPublicKeyIsInTrustList(remoteIdPublicKey, _trustedClientPublicKeys))
                                        {
                                            AsymmetricCipherKeyPair keyPair = MyProtocolCryptoHelper.GenerateMyECDHKeyPair();
                                            AuthStageInfo casi = new AuthStageInfo
                                            {
                                                LastRecvDatagramId = payload.DatagramId,
                                                LastRecvDatagramChangeId = payload.IsThisDatagramChangeId,
                                                RemoteIdAuthPublicKey = remoteIdPublicKey,
                                                RemoteKeyAgreementPublicKey = MyProtocolCryptoHelper.ECPublicKeyFromMyBytes(chp.ClientKeyAgreementPublicKeyX, chp.ClientKeyAgreementPublicKeyY),
                                                AuthStage = MyProtocolAuthStageEnum.CLIENT_SAY_HELLO,
                                                LocalKeyAgreementPublicKey = (ECPublicKeyParameters)keyPair.Public,
                                                LocalKeyAgreementPrivateKey = (ECPrivateKeyParameters)keyPair.Private
                                            };
                                            MyProtocolCryptoHelper.SecRandom.NextBytes(casi.RemoteIdAuthChallengeRandom);
                                            _authStages.Add(epStr, casi);
                                            byte[] challenge = MyProtocolCryptoHelper.CreateChallenge(casi.RemoteIdAuthChallengeRandom, casi.RemoteIdAuthPublicKey);
                                            BridgeHelloPayload bhp = new BridgeHelloPayload
                                            {
                                                ClientIdAuthChallengeLength = (ushort)challenge.Length,
                                                ClientIdAuthChallenge = challenge,
                                                BridgeIdAuthPublicKeyX = MyProtocolCryptoHelper.ECPublicKeyXToBytes(_ecBridgeIdAuthKeyPair.Public),
                                                BridgeIdAuthPublicKeyY = MyProtocolCryptoHelper.ECPublicKeyYToBytes(_ecBridgeIdAuthKeyPair.Public),
                                                BridgeKeyAgreementPublicKeyX = MyProtocolCryptoHelper.ECPublicKeyXToBytes(casi.LocalKeyAgreementPublicKey),
                                                BridgeKeyAgreementPublicKeyY = MyProtocolCryptoHelper.ECPublicKeyYToBytes(casi.LocalKeyAgreementPublicKey)
                                            };
                                            bhp.BridgeIdAuthPublicKeyXLength = (ushort)bhp.BridgeIdAuthPublicKeyX.Length;
                                            bhp.BridgeIdAuthPublicKeyYLength = (ushort)bhp.BridgeIdAuthPublicKeyY.Length;
                                            bhp.BridgeKeyAgreementPublicKeyXLength = (ushort)bhp.BridgeKeyAgreementPublicKeyX.Length;
                                            bhp.BridgeKeyAgreementPublicKeyYLength = (ushort)bhp.BridgeKeyAgreementPublicKeyY.Length;
                                            EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, bhp.WriteToBytes(), lcp.Cmd, payload.SessionId, ep);
                                            casi.AuthStage = MyProtocolAuthStageEnum.BRIDGE_SAY_HELLO;
                                            _logger.LogDebug($"bridge auth stage change to {casi.AuthStage}");
                                        }
                                        else
                                        {
                                            EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.ACCESS_DENIED, [], lcp.Cmd, payload.SessionId, ep);
                                            _logger.LogDebug("bridge found client is not in trusted list");
                                        }
                                    }
                                    else
                                    {
                                        EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.NOT_SUPPORT, [], lcp.Cmd, payload.SessionId, ep);
                                        _logger.LogDebug("bridge found client crypto suit was unsupportable");
                                    }
                                }
                                else
                                {
                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.ACCESS_DENIED, [], lcp.Cmd, payload.SessionId, ep);
                                    _logger.LogDebug($"bridge check cmd incorrect, except hello but {lcp.Cmd}");
                                }
                            }
                        }
                    }
                    else if (_validSessions.TryGetValue(payload.SessionId, out var value))//authed client
                    {
                        if ((!payload.IsThisDatagramChangeId && payload.DatagramId > value.LastRecvDatagramId) || (payload.IsThisDatagramChangeId && !value.LastRecvDatagramChangeId))
                        {
                            value.LastRecvDatagramId = payload.DatagramId;
                            value.LastRecvDatagramChangeId = payload.IsThisDatagramChangeId;
                            value.LastEP = ep;
                            byte[] data = payload.Data;
                            if (payload.IsEncrypted)
                            {
                                if (value.SessionKey != null)
                                {
                                    data = MyProtocolCryptoHelper.SM4Decrypt(payload.Data, value.SessionKey);
                                }
                                else
                                {
                                    string errStr = "会话密钥为空，无法解密";
                                    _logger.LogError(errStr);
                                    throw new ArgumentNullException(nameof(value.SessionKey), errStr);
                                }
                            }
                            switch (payload.DataType)
                            {
                                case PayloadTypeEnum.LINK_CONTROL:
                                    LinkControlPayload lcp = new LinkControlPayload(data);
                                    if (!lcp.IsResponse)//client request
                                    {
                                        _logger.LogDebug($"收到客户端lcp请求,cmd={lcp.Cmd}");
                                        switch (lcp.Cmd)
                                        {
                                            case LinkControlCmdEnum.SESSION_GOODBYE:
                                                EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, [], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey, true);
                                                break;
                                            case LinkControlCmdEnum.CONNECT_CONTROL_CHANNEL:
                                            case LinkControlCmdEnum.CONNECT_AUDIO_CHANNEL:
                                            case LinkControlCmdEnum.DISCONNECT_CONTROL_CHANNEL:
                                            case LinkControlCmdEnum.DISCONNECT_AUDIO_CHANNEL:
                                                int r1 = await _remoteDevLinkOp(_lccDlotDic[lcp.Cmd], lcp.Data.Length > 0 ? new BluetoothAddress(lcp.Data) : null);
                                                if (r1 == 1)
                                                {
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, [(byte)r1], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                else if (r1 < 0)
                                                {
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OPERATION_FAILED, [(byte)r1], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                else
                                                {
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.NOT_SUPPORT, [], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                break;
                                            case LinkControlCmdEnum.GET_REMOTE_DEV_LINK_STATUS:
                                                RemoteDevLinkStatusArgs rdlsa = await _getRemoteDevLinkStatus();
                                                if (rdlsa.OpResult == 1)
                                                {
                                                    DevLinkStatusPayload dlsp = new DevLinkStatusPayload() { IsAudioConnected = rdlsa.IsDevAudioConnected, IsCtrlConnected = rdlsa.IsDevAudioConnected };
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, dlsp.WriteToBytes(), lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                else
                                                {
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OPERATION_FAILED, [(byte)rdlsa.OpResult], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                break;
                                            case LinkControlCmdEnum.SET_REPEAT_TIMES:
                                                if (lcp.Data.Length > 0)
                                                {
                                                    value.RepeatTimes = lcp.Data[0];
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, [], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                else
                                                {
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.ARGUMENTS_ERROR, [], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                break;
                                            case LinkControlCmdEnum.DISCOVER_MY_DEVICES:
                                                IReadOnlyCollection<BtDevInfo>? result = await _discoverBtDevs(5);
                                                if (result != null)
                                                {
                                                    MyDiscoveredDevices mdd = new MyDiscoveredDevices(result, null);
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OK, mdd.WriteToBytes(), lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                else
                                                {
                                                    EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.OPERATION_FAILED, [], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                }
                                                break;
                                            default:
                                                EnqueueLinkControlResponsePayload(LinkControlResponseStatusCodeEnum.NOT_SUPPORT, [], lcp.Cmd, value.SessionId, value.LastEP, value.SessionKey);
                                                break;
                                        }
                                    }
                                    break;
                                case PayloadTypeEnum.DEV_CONTROL:
                                    _devCtrlTxQueue.Add(data);
                                    break;
                                case PayloadTypeEnum.DEV_AUDIO:
                                    _devAudioTxQueue.Add(data);
                                    break;
                            }
                        }
                    }
                }
            }
        }

        private async void SendDevCtrlDataAsync()
        {
            _sendCtrlRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        byte[] data = _devCtrlRxQueue.Take();
                        await Task.Run(() =>
                        {
                            foreach (var info in _validSessions.Values)
                            {
                                byte[] senddata = data;
                                if (info.SessionKey != null)
                                {
                                    senddata = MyProtocolCryptoHelper.SM4Encrypt(data, info.SessionKey);
                                }
                                if (info.LastEP != null)
                                {
                                    _waitToSendQueue.Add(new MyWaitToSendDataInfo(info.SessionId, false, false, info.UseCrypto, false, PayloadTypeEnum.DEV_CONTROL, senddata, info.LastEP));
                                }
                            }
                        });
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpBridgeSide SendDevCtrlDataAsync in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpBridgeSide SendDevCtrlDataAsync in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpBridgeSide SendDevCtrlDataAsync Excpetion:{ex.Message}");
            }
            _sendCtrlRunning = false;
        }
        public void SendDevConnectStatusChangedNotice(DevConnectStatusChangedNoticeTypeEnum noticeType)
        {
            _logger.LogDebug($"即将发送设备链接状态通知，noticeType={noticeType}");
            foreach (SessionInfo si in _validSessions.Values)
            {
                LinkControlPayload lcp = new LinkControlPayload(false, LinkControlCmdEnum.REMOTE_DEV_LINK_STATUS_CHANGED_NOTICE, [(byte)noticeType]);
                byte[] plBytes = lcp.WriteToBytes();
                if (si.SessionKey != null)
                {
                    plBytes = MyProtocolCryptoHelper.SM4Encrypt(plBytes, si.SessionKey);
                }
                if (si.LastEP != null)
                {
                    _waitToSendQueue.Add(new MyWaitToSendDataInfo(si.SessionId, false, false, si.UseCrypto, false, PayloadTypeEnum.LINK_CONTROL, plBytes, si.LastEP));
                }
            }
        }
        private async void SendDevAudioDataAsync()
        {
            _sendAudioRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        byte[] data = _devAudioRxQueue.Take();
                        await Task.Run(() =>
                        {
                            foreach (var info in _validSessions.Values)
                            {
                                byte[] senddata = data;
                                if (info.SessionKey != null)
                                {
                                    senddata = MyProtocolCryptoHelper.SM4Encrypt(data, info.SessionKey);
                                }
                                if (info.LastEP != null)
                                {
                                    _waitToSendQueue.Add(new MyWaitToSendDataInfo(info.SessionId, false, false, info.UseCrypto, false, PayloadTypeEnum.DEV_AUDIO, senddata, info.LastEP));
                                }
                            }
                        });
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpBridgeSide SendDevAudioDataAsync in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpBridgeSide SendDevAudioDataAsync in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpBridgeSide SendDevAudioDataAsync Excpetion:{ex.Message}");
            }
            _sendAudioRunning = false;
        }
        private async Task TakeQueueToSendAsync()
        {
            _takeQueueToSendRunning = true;
            try
            {
                while (!_cts.IsCancellationRequested)
                {
                    try
                    {
                        MyWaitToSendDataInfo info = _waitToSendQueue.Take();
                        int dataId = 1;
                        bool changeId = false;
                        int repeatTimes = _eachPayloadRepeatTimes;
                        if (info.SessionId == Guid.Empty)
                        {
                            string epStr = info.RemoteEndPoint.ToString();
                            if (_authStages.TryGetValue(epStr, out AuthStageInfo? asi))
                            {
                                dataId = asi.LastSendDatagramId;
                                changeId = dataId == 0;
                                asi.LastSendDatagramId = dataId + 1;
                                if (asi.LastSendDatagramId == int.MaxValue)
                                    asi.LastSendDatagramId = 0;
                                if (info.NeedRemoveSession)
                                {
                                    _authStages.Remove(epStr);
                                }
                            }
                            else
                            {
                                _logger.LogDebug($"未找到对方ip的认证阶段信息,rep={info.RemoteEndPoint}");
                            }
                        }
                        else
                        {
                            if (_validSessions.TryGetValue(info.SessionId, out SessionInfo? si))
                            {
                                dataId = si.LastSendDatagramId;
                                changeId = dataId == 0;
                                si.LastSendDatagramId = dataId + 1;
                                if (si.LastSendDatagramId == int.MaxValue)
                                    si.LastSendDatagramId = 0;
                                if (info.NeedRemoveSession)
                                {
                                    _validSessions.Remove(info.SessionId);
                                }
                                repeatTimes = si.RepeatTimes;
                            }
                            else
                            {
                                _logger.LogDebug($"未找到会话id,sessionId={info.SessionId}");
                            }
                        }
                        MyProtocolPayload pl = new MyProtocolPayload(info.SessionId, dataId, info.IsClientSent, info.IsResponse, info.IsEncrypted, changeId, info.IsHistoryReply, info.PayloadType, info.Data);
                        byte[] bytes = pl.WriteToBytes();
                        _ = Task.Run(async () =>
                        {
                            try
                            {
                                for (int i = 0; i < (info.PayloadType != PayloadTypeEnum.LINK_CONTROL ? repeatTimes : 1); i++)
                                {
                                    if (info.RemoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
                                        await _bridgeSock6.SendToAsync(bytes, info.RemoteEndPoint, _cts.Token);
                                    else
                                        await _bridgeSock4.SendToAsync(bytes, info.RemoteEndPoint, _cts.Token);
                                }
                            }
                            catch (SocketException se)
                            {
                                _logger.LogError(se, "发送时出现socket异常");
                                if (_validSessions.Remove(info.SessionId))
                                {
                                    _logger.LogInformation("由于发送失败，将此会话移除");
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.LogError(ex, "发送时出现异常");
                            }
                        });
                    }
                    catch (OperationCanceledException)
                    {
                        _logger.LogInformation($"UdpBridgeSide TakeQueueToSendAsync in-loop received OperationCancelExcpetion,exit");
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, $"UdpBridgeSide TakeQueueToSendAsync in-loop Excpetion:{ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"UdpBridgeSide TakeQueueToSendAsync Excpetion:{ex.Message}");
            }
            _takeQueueToSendRunning = false;
        }

        public void Dispose()
        {
            Stop();
        }
    }
}
